#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stddef.h>
#include <time.h>

#include "debug.h"
#include "sdmng.h"
#include "playback.h"
#include "record.h"
#include "mav.h"
#include "mav_db.h"

static struct mav_db_list g_mav_db;
void mav_db_print_list(void)
{
    struct mav_db_list *list = &g_mav_db;
    unsigned int db_size = 0;

    pthread_mutex_lock(&list->lock);
    db_size = sizeof(g_mav_db)+list->maxcnt*sizeof(struct mav_db_record);

    printf("---db record num = %u\n",list->num);
    printf("---db record max = %u\n",list->maxcnt);
    printf("---db record head = %d\n",list->head);

    if(list->num>0)
	printf("---record list:\n");
    int i = 0,j = 0,k = 0,s = 0;
    for(i=0,k=list->head;i<list->num;i++,k=list->reclist[k].next)
    {
	db_size += list->reclist[k].item_maxcnt*sizeof(struct mav_db_item);
	printf("[%02d]seq = %u\n",i,list->reclist[k].seq);
	printf("    date = %s\n",list->reclist[k].date);
	printf("    item_num = %u\n",list->reclist[k].item_num);
	printf("    item_maxcnt= %u\n",list->reclist[k].item_maxcnt);
	printf("    head= %d\n",list->reclist[k].item_head);
	printf("    next= %d\n",list->reclist[k].next);
	for(j=0,s=list->reclist[k].item_head;j<list->reclist[k].item_num;j++,s=list->reclist[k].item[s].next)
	{
	    printf("\t[%02d]seq = %u\n",j,list->reclist[k].item[s].seq);
	    printf("\t    fname= %s\n",list->reclist[k].item[s].fname);
	    printf("\t    starts= %ld\n",list->reclist[k].item[s].starts);
	    printf("\t    endts= %ld\n",list->reclist[k].item[s].endts);
	    printf("\t    next= %d\n",list->reclist[k].item[s].next);
	}
    }
    printf("+++mav db actual size = %d Bytes!\n",db_size);
    pthread_mutex_unlock(&list->lock);
}

static int parse_recname(const char *recname,char *dname,char *fname)
{
    int ret = 0;

    ret = sscanf(recname,MAV_MOUNT_DIR"/%[0-9]/%[0-9].mav",dname,fname);
    if(ret != 2)
	return -1;
    else 
    {
	strcat(fname,".mav");
	return 0;
    }
}

static int mav_db_init_sdfile(struct mav_db_list *list)
{
    struct sd_item_list *dirs = NULL;
    int ret = 0;

    ret = rsc_get_file_list(NULL,&dirs);
    if(ret < 0)
    {
	SLOG(ERR,"rsc_get_file_list sd root fail!\n");
	return -1;
    }
    int i,j;
    char fpath[64]={0};
    for(i=0;i<dirs->cnt;i++)
    {
	struct sd_item_list *files = NULL;
	
	if(dirs->item[i].type==TYPE_SD_DIR)
	{
	    ret = rsc_get_file_list(dirs->item[i].fname,&files);
	    if(ret < 0)
	    {
		SLOG(ERR,"rsc_get_file_list [%s] fail!\n",dirs->item[i].fname);
	    }
	    else
	    {
		for(j=0;j<files->cnt;j++)
		{
		    if(files->item[j].type == TYPE_SD_FILE)
		    {
			snprintf(fpath,sizeof(fpath),"%s/%s/%s",
				MAV_MOUNT_DIR,dirs->item[i].fname,files->item[j].fname);
			mav_db_add_item(fpath);
		    }
		}
		free(files);
	    }
	}
    }

    free(dirs);
    mav_db_print_list();
    return ret;
}

int mav_db_init_list(void)
{
    struct mav_db_list *plist = &g_mav_db;
    plist->reclist = calloc(MAV_DB_INITIAL_RECNUM,sizeof(struct mav_db_record));
    if(plist->reclist==NULL)
    {
	SLOG(ERR,"calloc mav_db reclist fail!\n");
	return -1;
    }
    plist->num = 0;
    plist->maxcnt = MAV_DB_INITIAL_RECNUM;
    plist->head = 0;
    pthread_mutex_init(&plist->lock,NULL);
    /*** init database ,scanning directory and files */
    return mav_db_init_sdfile(plist);
}
static int mav_db_find_record(const char *dname)
{
    struct mav_db_list *list = &g_mav_db;
    if(list==NULL||dname==NULL)
	return -1;
    int i = 0,j;
    int pos = -1;
    pthread_mutex_lock(&list->lock);
    for(i=0,j=list->head;i<list->num;i++,j=list->reclist[j].next)
    {
	if(!strcmp(dname,list->reclist[j].date))
	{
	    pos = j;
	    break;
	}
    }
    pthread_mutex_unlock(&list->lock);
    return pos;
}

static int mav_db_find_item(const char *recname,int *rec_pos)
{
    char dname[16];
    char fname[16];

    if(recname==NULL)
	return -1;
    if(parse_recname(recname,dname,fname)<0)
	return -1;
    int pos = mav_db_find_record(dname);
    if(pos < 0)
	return -1;

    *rec_pos = pos;
    struct mav_db_list *list = &g_mav_db;
    struct mav_db_record *record = &list->reclist[pos];
    int item_pos = -1,i = 0,j;
    pthread_mutex_lock(&list->lock);
    for(i=0,j=record->item_head;i<record->item_num;i++,j=record->item[j].next)
    {
	if(!strcmp(fname,record->item[j].fname))
	{
	    item_pos = j;
	    break;
	}
    }
    pthread_mutex_unlock(&list->lock);
    return item_pos;
}

int mav_db_add_record(const char *dname)
{
    struct mav_db_list *list = &g_mav_db;
    if(list==NULL||dname==NULL)
	return -1;

    int last_pos,pos = -1;
    pos = mav_db_find_record(dname);
    if(pos >= 0)
    {
	//SLOG(PRI,"dname %s exists,pos = %d!\n",dname,pos);
	return pos;
    }
    pthread_mutex_lock(&list->lock);
    last_pos = (list->head+list->num-1)%list->maxcnt;
    if(list->num+1==list->maxcnt)
    {
	SLOG(PRI,"increase mav db record size,now is %d!\n",list->num);
	list->reclist = realloc(list->reclist,(list->maxcnt+MAV_DB_INITIAL_RECNUM)*sizeof(struct mav_db_record));
	list->maxcnt = list->maxcnt+MAV_DB_INITIAL_RECNUM;
    }
    pos = (list->head+list->num)%list->maxcnt;
    list->reclist[last_pos].next = pos;
    list->reclist[pos].seq = pos;
    strcpy(list->reclist[pos].date,dname);
    list->reclist[pos].item_num = 0;
    list->reclist[pos].item = calloc(MAV_DB_INITIAL_ITEMNUM,sizeof(struct mav_db_item));
    list->reclist[pos].item_maxcnt = MAV_DB_INITIAL_ITEMNUM;
    list->reclist[pos].item_head = 0;
    //list->reclist[pos].next = (pos+1)%list->maxcnt;
    list->reclist[pos].next = -1;
    if(list->reclist[pos].item==NULL)
    {
	SLOG(ERR,"calloc mav_db_item fail!\n");
	pos = -1;
	goto add_rec_end;
    }
    list->num++;

add_rec_end: 
    pthread_mutex_unlock(&list->lock);
    return pos;
}
int mav_db_del_record(const char *dname)
{
    struct mav_db_list *list = &g_mav_db;
    if(dname==NULL)
	return -1;

    int ret = 0;
    int pos = -1;
    pos = mav_db_find_record(dname);
    if(pos >= 0)
    {
	pthread_mutex_lock(&list->lock);
	if(list->reclist[pos].item)
	    free(list->reclist[pos].item);
	list->head = list->reclist[pos].next;
	memset(&list->reclist[pos],0,sizeof(struct mav_db_record));
	if(list->num>0)
	    list->num--;
	if(list->num==0)
	    list->head = 0;
	pthread_mutex_unlock(&list->lock);
    }
    else
    {
	SLOG(ERR,"dname %s doesnot exist!\n",dname);
	ret = -1;
    }
    return ret;
}

int mav_db_add_item(const char *recname)
{
    char dname[16];
    char fname[16];
    int pos = -1;
    time_t start,end;

    if(recname==NULL)
	return -1;
    else if(mav_get_tszone(recname,&start,&end)<0)
    {
	SLOG(ERR,"mav_get_tszone file:%s fail\n",recname);
	return -1;
    }
    if(parse_recname(recname,dname,fname)<0)
    {
	SLOG(ERR,"parse_recname %s fail!\n",recname);
	return -1;
    }
    pos = mav_db_add_record(dname);
    if(pos < 0)
    {
	SLOG(ERR,"mav_db_add_record dname %s fail!\n",dname);
	return -1;
    }

    SLOG(PRI,"dname %s pos= %d,recname = %s\n",dname,pos,recname);
    struct mav_db_list *list = &g_mav_db;
    struct mav_db_record *record = &list->reclist[pos];
    struct mav_db_item *pitem = NULL;
    int last_pos ,item_pos = -1;

    pthread_mutex_lock(&list->lock);
    last_pos = (record->item_head+record->item_num-1)%record->item_maxcnt;
    if(record->item_num+1 >= record->item_maxcnt)
    {// increase item
	record->item = realloc(record->item,(record->item_maxcnt+MAV_DB_INITIAL_ITEMNUM)*sizeof(struct mav_db_item));
	record->item_maxcnt += MAV_DB_INITIAL_ITEMNUM;
    }
    item_pos = (record->item_head+record->item_num)%record->item_maxcnt;
    record->item[last_pos].next = item_pos;
    pitem = &record->item[item_pos];
    pitem->seq = item_pos;
    strcpy(pitem->fname,fname);
    pitem->starts = start;
    pitem->endts = end;
    //pitem->next = (item_pos+1)%record->item_maxcnt;
    pitem->next = -1;
    record->item_num++;
    pthread_mutex_unlock(&list->lock);

    mav_db_print_list(); /* just for debug */
    return 0;
}
int mav_db_del_item(const char *recname)
{
    char dname[16];
    char fname[16];
    int pos = -1,item_pos = -1;
    int item_num = -1;

    if(recname==NULL)
	return -1;
    if(parse_recname(recname,dname,fname)<0)
    {
	SLOG(ERR,"parse_recname %s fail!\n",recname);
	return -1;
    }
    item_pos = mav_db_find_item(recname,&pos);
    if(item_pos >= 0)
    {
	struct mav_db_list *list = &g_mav_db;
	struct mav_db_record *record = &list->reclist[pos];
	struct mav_db_item *pitem = NULL;

	pthread_mutex_lock(&list->lock);
	pitem = &record->item[item_pos];
	record->item_head = pitem->next;
	memset(pitem,0,sizeof(struct mav_db_item));
	if(record->item_num>0)
	    record->item_num--;
	item_num = record->item_num;
	if(item_num==0)
	    record->item_head = 0;
	pthread_mutex_unlock(&list->lock);
    }
    if(item_num==0)
	mav_db_del_record(dname);

    mav_db_print_list(); /* just for debug */
    return 0;
}

static int mav_db_seek_file(time_t tstart,char *filename,time_t *ptstart,time_t *ptend)
{
    if(filename==NULL||ptstart==NULL||ptend==NULL)
	return -1;

    struct tm mytm;
    struct tm *ptm = localtime_r(&tstart,&mytm);
    char dname[16];
    snprintf(dname,sizeof(dname),"%04d%02d%02d",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday);
    int rec_pos = -1;
    rec_pos = mav_db_find_record(dname);
    if(rec_pos < 0)
    {
	SLOG(ERR,"seek dname %s not find!\n",dname);
	return -1;
    }
    int ret = 0,i = 0,j = 0;
    struct mav_db_list *list = &g_mav_db;
    struct mav_db_record *record = &list->reclist[rec_pos];
    struct mav_db_item *pitem = NULL;
    int last_pos ,item_pos = -1;

    pthread_mutex_lock(&list->lock);
    item_pos = record->item_head;
    last_pos = (record->item_head+record->item_num-1)%record->item_maxcnt;
    pitem = &record->item[item_pos];
    if(tstart <= pitem->starts)
    {// first item
	sprintf(filename,"%s/%s/%s",MAV_MOUNT_DIR,dname,pitem->fname);
	*ptstart = pitem->starts;
	*ptend = pitem->endts;
	goto seek_fname;
    }
    pitem = &record->item[last_pos];
    if(tstart >= pitem->endts)
    {// last item
	SLOG(ERR,"tstart = %ld,item_num = %d,last_pos = %d,last item file:%s,endts:%ld!\n",
		tstart,record->item_num,last_pos,pitem->fname,pitem->endts);
	ret = -1;
	goto seek_fname;
    } 
    for(i=0,j=record->item_head;i<record->item_num;i++,j=record->item[j].next)
    {
	//item_pos = (record->item_head+i)%record->item_maxcnt;
	pitem = &record->item[j];
	if(tstart<pitem->endts)
	{
	    sprintf(filename,"%s/%s/%s",MAV_MOUNT_DIR,dname,pitem->fname);
	    *ptstart = pitem->starts;
	    *ptend = pitem->endts;
	    break;
	}
    }
seek_fname:
    pthread_mutex_unlock(&list->lock);
    return ret;
}

/** directly to read frame from FILE HANDLE,we have seek it */
FILE *mav_db_get_file(time_t tstart,int direction,time_t *pnstart)
{
    char filename[64]={0};
    FILE *fp = NULL;
    time_t ftstart,ftend;

    if(mav_db_seek_file(tstart,filename,&ftstart,&ftend)<0)
    {
	SLOG(WRN,"mav_db_seek_file in tstart [%ld] fail,try current recfile!\n",tstart);
	fp = mav_seek_current(tstart,&ftstart,&ftend);
    }
    else
    {
	SLOG(PRI,"seek time[%ld] filename:%s\n",tstart,filename);
	fp = mav_seek_frame(filename,tstart);
    }
    if(fp!=NULL && pnstart!=NULL)
    {
	if(direction==PLAYBACK_DIR_BACKWARD)
	    *pnstart = ftstart;
	else
	    *pnstart = ftend;
    }
    return fp;
}

void mav_db_exit_list(void)
{
    int i = 0;
    struct mav_db_list *list = &g_mav_db;
    for(i=0;i<list->num;i++)
    {
	if(list->reclist[i].item)
	    free(list->reclist[i].item);
    }

    pthread_mutex_destroy(&list->lock);
    if(list->reclist)
	free(list->reclist);
    memset(list,0,sizeof(struct mav_db_list));
}

int mav_db_build_timeline(st_mav_db_timeline *timeline)
{
    if(timeline == NULL)
    {
	SLOG(ERR,"timeline is null!\n");
	return -1;
    }
    memset(timeline,0,sizeof(st_mav_db_timeline));

    struct mav_db_list *list = &g_mav_db;
    int i = 0,j = 0,k = 0,s = 0;
    int cnt = 0;

    pthread_mutex_lock(&list->lock);
    for(i=0,k=list->head;i<list->num;i++,k=list->reclist[k].next)
    {
	for(j=0,s=list->reclist[k].item_head;j<list->reclist[k].item_num;j++,s=list->reclist[k].item[s].next)
	{
	    if(cnt==0)
	    {
		timeline->section[cnt].tstart = list->reclist[k].item[s].starts;
		timeline->section[cnt].tend = list->reclist[k].item[s].endts;
		cnt++;
	    }
	    else if(timeline->section[cnt-1].tend+60<list->reclist[k].item[s].starts)
	    {
		timeline->section[cnt].tstart = list->reclist[k].item[s].starts;
		timeline->section[cnt].tend = list->reclist[k].item[s].endts;
		cnt++;
	    }
	    else 
	    {
		timeline->section[cnt-1].tend = list->reclist[k].item[s].endts;
	    }
	    if(cnt >= MAV_TIMELINE_MAX)
	    {
		SLOG(WRN,"timeline section number[%d] too much!\n",cnt);
		break;
	    }
	}
	if(cnt >= MAV_TIMELINE_MAX)
	    break;
    }
    timeline->num = cnt;
    pthread_mutex_unlock(&list->lock);

    return 0;
}

int mav_db_build_dateline(st_mav_db_dateline *dateline)
{
    if(dateline == NULL)
    {
	SLOG(ERR,"dateline is null!\n");
	return -1;
    }
    memset(dateline,0,sizeof(st_mav_db_dateline));

    struct mav_db_list *list = &g_mav_db;
    int i = 0,k = 0;
    int cnt = 0;
    st_mav_date date;
    int tmp,inflag = 0;
    char cur_recdir[64]={0};

    rsc_get_cur_recdir(cur_recdir,sizeof(cur_recdir));
    pthread_mutex_lock(&list->lock);
    for(i=0,k=list->head;i<list->num;i++,k=list->reclist[k].next)
    {
	if(strstr(cur_recdir,list->reclist[k].date))
	    inflag = 1;
	tmp = atoi(list->reclist[k].date);
	date.year = tmp/10000;
	date.month = tmp%10000/100;
	date.day = tmp%100;
	dateline->section[cnt++] = date;
	if(cnt >= MAV_TIMELINE_MAX)
	{
	    SLOG(WRN,"dateline section number[%d] too much!\n",cnt);
	    break;
	}
    }
    if(inflag==0 && cnt<MAV_TIMELINE_MAX && strrchr(cur_recdir,'/'))
    {/* this should almost never happen */
	SLOG(PRI,"cur_recdir:%s\n",cur_recdir);
	tmp = atoi(strrchr(cur_recdir,'/')+1);
	date.year = tmp/10000;
	date.month = tmp%10000/100;
	date.day = tmp%100;
	dateline->section[cnt++] = date;
    }
    dateline->num = cnt;
    pthread_mutex_unlock(&list->lock);

    return 0;
}

int mav_db_build_daytline(st_mav_date date,st_mav_db_timeline *daytline)
{
    if(daytline == NULL)
    {
	SLOG(ERR,"daytline is null!\n");
	return -1;
    }
    memset(daytline,0,sizeof(st_mav_db_timeline));

    struct mav_db_list *list = &g_mav_db;
    int i = 0,j = 0,k = 0,s = 0;
    int cnt = 0;
    char tmp[16]={0};

    snprintf(tmp,sizeof(tmp),"%04d%02d%02d",date.year,date.month,date.day);
    pthread_mutex_lock(&list->lock);
    for(i=0,k=list->head;i<list->num;i++,k=list->reclist[k].next)
    {
	if(!strcmp(tmp,list->reclist[k].date))
	{// find the day
	    for(j=0,s=list->reclist[k].item_head;j<list->reclist[k].item_num;j++,s=list->reclist[k].item[s].next)
	    {
		if(cnt==0)
		{
		    daytline->section[cnt].tstart = list->reclist[k].item[s].starts;
		    daytline->section[cnt].tend = list->reclist[k].item[s].endts;
		    cnt++;
		}
		else if(daytline->section[cnt-1].tend+60<list->reclist[k].item[s].starts)
		{
		    daytline->section[cnt].tstart = list->reclist[k].item[s].starts;
		    daytline->section[cnt].tend = list->reclist[k].item[s].endts;
		    cnt++;
		}
		else 
		{
		    daytline->section[cnt-1].tend = list->reclist[k].item[s].endts;
		}
		if(cnt >= MAV_TIMELINE_MAX)
		{
		    SLOG(ERR,"daytline section number[%d] too much!\n",cnt);
		    break;
		}
	    }
	    break;
	}
    }

    char cur_recdir[64]={0};
    time_t tstart,tend;

    rsc_get_cur_recdir(cur_recdir,sizeof(cur_recdir));
    if(strstr(cur_recdir,tmp) && cnt < MAV_TIMELINE_MAX 
	    && mav_get_current_tszone(&tstart,&tend)==0)
    {
	SLOG(PRI,"get current record file timeline:%ld - %ld!\n",tstart,tend);
	daytline->section[cnt].tstart = tstart;
	daytline->section[cnt].tend = tend;
	cnt++;
    }
    daytline->num = cnt;
    pthread_mutex_unlock(&list->lock);

    return 0;
}

